package com.gitee.wsl.mathematics.matrix.mat4.ext

import com.gitee.wsl.mathematics.matrix.ext.setIdentity
import com.gitee.wsl.mathematics.matrix.mat4.MutableMatrix4


/**
 * Sets the matrix to an orthographic projection.
 *
 * @param left The left clipping plane
 * @param right The right clipping plane
 * @param bottom The bottom clipping plane
 * @param top The top clipping plane
 * @param near The near clipping plane
 * @param far The far clipping plane
 * @return this matrix
 */
fun<T:Number> MutableMatrix4<T, *, *>.setToOrthographic(
    left: T, right: T,
    bottom: T, top: T,
    near: T, far: T
){
    if (left == right) {
        throw IllegalArgumentException("left == right")
    }
    if (bottom == top) {
        throw IllegalArgumentException("bottom == top")
    }
    if (near == far) {
        throw IllegalArgumentException("near == far")
    }

    val width = 1f / (right - left)
    val height = 1f / (top - bottom)
    val depth = 1f / (far - near)
    val x = 2f * width
    val y = 2f * height
    val z = -2f * depth
    val tx = -(right + left) * width
    val ty = -(top + bottom) * height
    val tz = (far + near) * depth
    /*
    m00 = x
    m10 = zero
    m20 = zero
    m30 = zero
    m01 = zero
    m11 = y
    m21 = zero
    m31 = zero
    m02 = zero
    m12 = zero
    m22 = z
    m32 = zero
    m03 = tx
    m13 = ty
    m23 = tz
    m33 = one
    */

    m[0].set(x,   zero, zero, tx )
    m[1].set(zero,  y , zero, ty )
    m[2].set(zero,zero, z   , tz )
    m[3].set(zero,zero, zero ,one)

}

/** Creates a matrix for projecting two-dimensional coordinates onto the screen.
 *
 *  @see gtc_matrix_transform
 *  @see - glm.ortho(left: Float, right: Float, bottom: Float, top: Float, zNear: Float, zFar: Float)
 *  @see <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluOrtho2D.xml">gluOrtho2D man page</a>
 */
fun<T:Number> MutableMatrix4<T, *, *>.setToOrtho(left: T, right: T, bottom: T, top: T) {
    setIdentity()
    this[0, 0] = 2f / (right - left)
    this[1, 1] = 2f / (top - bottom)
    this[2, 2] = -one
    this[3, 0] = -(right + left) / (right - left)
    this[3, 1] = -(top + bottom) / (top - bottom)
}

/**
 * Inplace operation: Applies an orthographic projection transform to this matrix.
 */
fun<T:Number> MutableMatrix4<T, *, *>.orthographic(left: T, right: T,
                                                   bottom: T, top: T,
                                                   near: T, far: T,
                                                   depthRange: DepthRange){
    if (left == right) {
        throw IllegalArgumentException("left == right")
    }
    if (bottom == top) {
        throw IllegalArgumentException("bottom == top")
    }
    if (near == far) {
        throw IllegalArgumentException("near == far")
    }

    val w = 1.0f / (right - left)
    val h = 1.0f / (top - bottom)
    val d = 1.0f / (far - near)

    val x = (right + left) * w
    val y = (top + bottom) * h

    val z: T
    val zd: T
    when (depthRange) {
        DepthRange.NEGATIVE_ONE_TO_ONE -> {
            z = (far + near) * d
            zd = -2 * d
        }
        DepthRange.ZERO_TO_ONE -> {
            z = near * d
            zd = -1 * d
        }
    }

    return mul(
        2 * w, zero, zero, -x,
        zero, 2 * h, zero, -y,
        zero, zero, zd, -z,
        zero, zero, zero, one
    )
}

enum class DepthRange {
    NEGATIVE_ONE_TO_ONE,
    ZERO_TO_ONE
}